package com.power.doc.builder;

import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.power.common.util.CollectionUtil;
import com.power.doc.model.ApiConfig;
import com.power.doc.model.ApiDoc;
import com.power.doc.model.ApiMethodDoc;
import com.power.doc.model.ApiParam;
import com.power.doc.model.ApiReqHeader;
import com.power.doc.model.yapi.ResponseBodyTypeEnum;
import com.power.doc.model.yapi.YApiCat;
import com.power.doc.model.yapi.YApiForm;
import com.power.doc.model.yapi.YApiHeader;
import com.power.doc.model.yapi.YApiInterface;
import com.power.doc.model.yapi.YApiPathVariable;
import com.power.doc.model.yapi.YApiProject;
import com.power.doc.model.yapi.YApiQuery;
import com.power.doc.model.yapi.YApiSdk;
import com.power.doc.template.IDocBuildTemplate;
import com.power.doc.template.SpringBootDocBuildTemplate;
import com.thoughtworks.qdox.JavaProjectBuilder;

public class OpenYapiBuilder {
    
    /**
     * 构建yapi json
     *
     * @param config 配置文件
     */
    public static void buildYapi(ApiConfig config) {
        DocBuilderTemplate builderTemplate = new DocBuilderTemplate();
        builderTemplate.checkAndInitForGetApiData(config);
        JavaProjectBuilder javaProjectBuilder = new JavaProjectBuilder();
        ProjectDocConfigBuilder configBuilder = new ProjectDocConfigBuilder(config, javaProjectBuilder);
        yapiCreate(config, configBuilder);
    }

    /**
     * Only for smart-doc maven plugin and gradle plugin.
     *
     * @param config         ApiConfig Object
     * @param projectBuilder QDOX avaProjectBuilder
     */
    public static void buildYapiCollection(ApiConfig config, JavaProjectBuilder projectBuilder) {
        DocBuilderTemplate builderTemplate = new DocBuilderTemplate();
        builderTemplate.checkAndInitForGetApiData(config);
        ProjectDocConfigBuilder configBuilder = new ProjectDocConfigBuilder(config, projectBuilder);
        yapiCreate(config, configBuilder);
    }
    
    private static void yapiCreate(ApiConfig config, ProjectDocConfigBuilder configBuilder) {
        try {
            config.setParamsDataToTree(true);
            YApiProject project = YApiSdk.getProjectInfo(config.getOpenUrl(), config.getAppToken());
            IDocBuildTemplate docBuildTemplate = new SpringBootDocBuildTemplate();
            List<ApiDoc> apiDocList = docBuildTemplate.getApiData(configBuilder);
            if (!CollectionUtil.isEmpty(apiDocList)) {
                List<YApiCat> apiCats = YApiSdk.listCategories(config.getOpenUrl(), config.getAppToken());
                for (ApiDoc apiDoc : apiDocList) {
                    YApiCat cat = getCat(apiCats, apiDoc);
                    if (cat == null) {
                        cat = YApiSdk.addCategory(config.getOpenUrl(), config.getAppToken(), project.get_id().toString(), apiDoc.getName());
                    }
                    List<ApiMethodDoc> methodDocs = apiDoc.getList();
                    for (ApiMethodDoc methodDoc : methodDocs) {
                        YApiInterface yApiInterface = convert(methodDoc, cat, config.getAppToken());
                        YApiSdk.saveInterface(config.getOpenUrl(), yApiInterface);
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    
    private static YApiInterface convert(ApiMethodDoc methodDoc, YApiCat cat, String token) {
        YApiInterface apiInterface = new YApiInterface();
        apiInterface.setCatid(cat.get_id().toString());
        apiInterface.setDesc(methodDoc.getDetail());
////        apiInterface.setMessage(message);
        apiInterface.setMethod(methodDoc.getType());
        apiInterface.setPath(methodDoc.getPath());
        apiInterface.setReq_body_form(convertForm(methodDoc));
        apiInterface.setReq_body_is_json_schema(isReqJson(methodDoc));
        apiInterface.setReq_body_other(genReqJson(methodDoc));
        apiInterface.setReq_body_type(apiInterface.getReq_body_is_json_schema()? ResponseBodyTypeEnum.JSON.getValue():ResponseBodyTypeEnum.RAW.getValue());
        apiInterface.setReq_headers(convertHeader(methodDoc));
        apiInterface.setReq_params(convertPathParam(methodDoc));
        apiInterface.setReq_query(convertQueryParam(methodDoc));
        apiInterface.setRes_body(genResJson(methodDoc));
        apiInterface.setRes_body_is_json_schema(true);
        apiInterface.setTitle(methodDoc.getDesc());
        apiInterface.setToken(token);
        return apiInterface;
    }
    
    private static List<YApiPathVariable>  convertPathParam(ApiMethodDoc methodDoc){
        List<ApiParam> apiParams = methodDoc.getPathParams();
        if (CollectionUtil.isNotEmpty(apiParams)) {
            List<YApiPathVariable> pathVariables = new ArrayList<>();
            for (ApiParam apiParam : apiParams) {
                YApiPathVariable pathVariable = new YApiPathVariable();
                pathVariable.setDesc(apiParam.getDesc());
                pathVariable.setName(apiParam.getField());
                pathVariables.add(pathVariable);
            }
            return pathVariables;
        }
        return null;
    }
    private static List<YApiQuery>  convertQueryParam(ApiMethodDoc methodDoc){
        List<ApiParam> apiParams = methodDoc.getPathParams();
        if (CollectionUtil.isNotEmpty(apiParams)) {
            List<YApiQuery> pathVariables = new ArrayList<>();
            for (ApiParam apiParam : apiParams) {
                YApiQuery pathVariable = new YApiQuery();
                pathVariable.setDesc(apiParam.getDesc());
                pathVariable.setName(apiParam.getField());
                pathVariable.setRequired(String.valueOf(apiParam.isRequired()));
                pathVariables.add(pathVariable);
            }
            return pathVariables;
        }
        return null;
    }
    private static List<YApiForm> convertForm(ApiMethodDoc methodDoc){
        if (isReqForm(methodDoc)) {
            List<ApiParam> apiParams = methodDoc.getRequestParams();
            if (CollectionUtil.isNotEmpty(apiParams)) {
                List<YApiForm> apiForms = new ArrayList<>();
                for (ApiParam apiParam : apiParams) {
                    YApiForm apiForm = new YApiForm();
                    apiForm.setDesc(apiParam.getDesc());
                    apiForm.setName(apiParam.getField());
                    apiForm.setRequired(String.valueOf(apiParam.isRequired()));
                    apiForm.setType(changeType(apiParam.getType()));
                    apiForms.add(apiForm);
                }
                return apiForms;
            }
        }
        return null;
    }
    
    private static List<YApiHeader>  convertHeader(ApiMethodDoc methodDoc) {
        List<ApiReqHeader> apiReqHeaders = methodDoc.getRequestHeaders();
        List<YApiHeader>  apiHeaders = new ArrayList<YApiHeader>();
        if(isReqJson(methodDoc)) {
            apiHeaders.add(YApiHeader.json());
        } else {
            apiHeaders.add(YApiHeader.form());
        }
        if (!CollectionUtil.isEmpty(apiReqHeaders)) {
            for (ApiReqHeader apiReqHeader : apiReqHeaders) {
                YApiHeader apiHeader = new YApiHeader(apiReqHeader.getName(), apiReqHeader.getValue());
                apiHeaders.add(apiHeader);
            }
        }
        return apiHeaders;
    }
    
    private static boolean isReqJson(ApiMethodDoc methodDoc) {
        String contentType = methodDoc.getContentType();
        if (contentType.contains("application/x-www-form-urlencoded")) {
            return false;
        } else {
            return true;
        }
    }
    private static boolean isReqForm(ApiMethodDoc methodDoc) {
        String contentType = methodDoc.getContentType();
        if (contentType.contains("application/x-www-form-urlencoded")) {
            return true;
        } else {
            return false;
        }
    }
    private static String genReqJson(ApiMethodDoc methodDoc) {
        List<ApiParam> apiParams = methodDoc.getRequestParams();
        return generateJson(apiParams);
    }
    private static String generateJson(List<ApiParam> responseParams) {
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("type", "object");
        JsonObject properties = new JsonObject();
        jsonObject.add("properties", properties);
        JsonArray array = new JsonArray();
        responseParams.stream().forEach(apiParam -> {
            getTypeAndPropertiesJson(apiParam, properties);
            if (apiParam.isRequired()) {
                array.add(apiParam.getField());
            }
        });
        jsonObject.add("required", array);
        return new Gson().toJson(jsonObject);
    }
    private static void getTypeAndPropertiesJson(ApiParam param, JsonObject properties) {
        JsonObject field = new JsonObject();
        properties.add(param.getField(), field);
        if (param.getChildren() != null && param.getChildren().size() > 0) {
            if (param.getType().equals("object")) {
                field.addProperty("type", changeType(param.getType()));
                field.addProperty("description", param.getDesc());
                JsonObject childProperties = new JsonObject();
                field.add("properties", childProperties);
                param.getChildren().forEach( childP-> {
                    getTypeAndPropertiesJson(childP, childProperties);
                });
            } else if (param.getType().equals("array")) {
                field.addProperty("type", changeType(param.getType()));
                field.addProperty("description", param.getDesc());
                JsonObject items = new JsonObject();
                field.add("items", items);
                JsonObject childProperties = new JsonObject();
                items.addProperty("type", "object");
                items.add("properties", childProperties);
                param.getChildren().forEach(childP -> {
                    getTypeAndPropertiesJson(childP, childProperties);
                });
            }
        } else {
            field.addProperty("type", changeType(param.getType()));
            field.addProperty("description", param.getDesc());
        }
    }
    /**
     * 将字段类型转换为yapi的字段类型
     *
     * @param type java type
     * @return String
     */
    private static String changeType(String type) {
        switch (type) {
            case "boolean":
                return "boolean";
            case "int32":
                return "integer";
            case "int64":
                return "number";
            default:
                return type;
        }
    }
    private static String genResJson(ApiMethodDoc methodDoc) {
        List<ApiParam> apiParams = methodDoc.getResponseParams();
        return generateJson(apiParams);
    }
    
    
    private static YApiCat getCat(List<YApiCat> apiCats, ApiDoc apiDoc) {
        for (YApiCat apiCat : apiCats) {
            if (apiCat.getName().equals(apiDoc.getName())) {
                return apiCat;
            }
        }
        return null;
    }
}
